#include "s3e.h"
#include "Iw2D.h"
#include <list>
#include <vector>
#include "ui_menu.h"
#include "ui_page.h"
#include "ui_widgets.h"
#include "block.h"
#include "grid.h"
#include "swipe.h"
#include "buttonfunctions.h"
#include "engine.h"
#include "game.h"

extern char g_StringBuffer[256];
extern int BlockSize;

Game* Game::m_Singleton = NULL;

static const float minMoveX = 3;
void Game::Update( float a_DT )
{
	switch (m_GameState)
	{
	case Game::NORMAL:
		{
			m_SwipeObject->Update(a_DT);

			if (!m_SwipeObject->GetSwiping())
			{
				m_CanRotate = true;
			}

			if (s3ePointerGetTouchState(0) & S3E_POINTER_STATE_RELEASED)
			{
				m_IsPerformingSwipe = false;
			}

			CIwSVec2 swipeVec = m_SwipeObject->GetSwipeMovement();

			if (abs(swipeVec.x) > abs(swipeVec.y))
			{
				if (swipeVec.x > 0)
				{
					//m_SwipeObject->Reset();
					m_Grid->MoveCurrentStructure(Grid::RIGHT);
				}
				else if (swipeVec.x < 0)
				{
					//m_SwipeObject->Reset();
					m_Grid->MoveCurrentStructure(Grid::LEFT);
				}

				m_IsPerformingSwipe = true;
			}
			else
			{
				if (!m_IsPerformingSwipe && m_CanRotate)
				{
					if (swipeVec.y > 0)
					{
						//m_SwipeObject->Reset();
						m_Grid->RotateCurrentStructure(Grid::LEFT);
						m_CanRotate = false;
						m_IsPerformingSwipe = true;		
					}
					else if (swipeVec.y < 0)
					{
						//m_SwipeObject->Reset();
						m_Grid->RotateCurrentStructure(Grid::RIGHT);
						m_CanRotate = false;
						m_IsPerformingSwipe = true;
					}

				}
			}

// 			char vecstring[128];
// 			sprintf(vecstring, "%i, %i", swipeVec.x, swipeVec.y);
// 			Iw2DDrawString(vecstring, CIwSVec2(0, 70), CIwSVec2(500, 500), IW_2D_FONT_ALIGN_LEFT, IW_2D_FONT_ALIGN_TOP);

			m_Grid->Update(a_DT);		

			// If the preview area is touched, move the block all the way down.
			if (s3ePointerGetTouchState(0) & S3E_POINTER_STATE_PRESSED)
			{
				if (s3ePointerGetTouchX(0) > m_PreviewBGTopLeft.x && s3ePointerGetTouchX(0) < m_PreviewBGTopLeft.x + m_PreviewBGSize.x &&
					s3ePointerGetTouchY(0) > m_PreviewBGTopLeft.y && s3ePointerGetTouchY(0) < m_PreviewBGTopLeft.y + m_PreviewBGSize.y)
				{
					// Update the grid while the current block can still move...
					while (m_Grid->BlockWasMoved())
					{
						// Update the grid and move the current block each time.
						m_Grid->Update(m_BlockMoveInterval);
					}
				}
			}

			break;
		}
	case Game::LINEANIMATING:
		{
			// Animate line.
			break;
		}
	case Game::GAMEOVER:
		{
			m_GameOverMenu->Update(a_DT);
			break;
		}
	case Game::PAUSED:
		{
			m_PauseMenu->Update(a_DT);
			break;
		}
	}

	// Draw the block preview and the preview area's background.
	Iw2DDrawImage(m_PreviewBG, m_PreviewBGTopLeft, m_PreviewBGSize );
	CIw2DImage* NextBlockImage = m_Grid->GetBlockTypeImage( m_Grid->GetNextBlockType() );
	bool* NextBlockPattern = &m_Grid->m_BlockPatterns[m_Grid->GetNextBlockIndex()][m_Grid->GetNextBlockRotation()][0][0];
	for (int y = 0; y < 4; ++y)
	{
		for (int x = 0; x < 4; ++x)
		{
			if (NextBlockPattern[y + x * 4] == true)
			{
				CIwSVec2 BlockPos(	int16(m_PreviewTopLeft.x + 1 + (x * (BlockSize + 1))), 
									int16(m_PreviewTopLeft.y + 1 + (y * (BlockSize + 1))) );
				Iw2DDrawImage(NextBlockImage, CIwSVec2(BlockPos.x, BlockPos.y), CIwSVec2(BlockSize, BlockSize));
			}
		}
	}


	// Draw score.
	sprintf(g_StringBuffer, "score\n%i", m_Score);
	Iw2DDrawString(g_StringBuffer, CIwSVec2( int16(TO_SCREENX(432.0f)), int16(TO_SCREENY(523.0f)) ), 
									CIwSVec2( int16(TO_SCREENX(196.0f)), int16(TO_SCREENY(116.0f)) ), 
									IW_2D_FONT_ALIGN_CENTRE, 
									IW_2D_FONT_ALIGN_CENTRE);
	sprintf(g_StringBuffer, "high\n%i", Engine::GetSingletonPtr()->GetCurrentHighscore());
	Iw2DDrawString(g_StringBuffer, CIwSVec2( int16(TO_SCREENX(432.0f)), int16(TO_SCREENY(673.0f)) ), 
									CIwSVec2( int16(TO_SCREENX(196.0f)), int16(TO_SCREENY(116.0f)) ), 
									IW_2D_FONT_ALIGN_CENTRE, 
									IW_2D_FONT_ALIGN_CENTRE);
	sprintf(g_StringBuffer, "next");
	Iw2DDrawString(g_StringBuffer, m_PreviewBGTopLeft,
									CIwSVec2(m_PreviewBGSize.x, int16(TO_SCREENY(54.0f))), 
									IW_2D_FONT_ALIGN_CENTRE, 
									IW_2D_FONT_ALIGN_CENTRE);

	// Check that is also performed a few lines ahead, but this is to separate updating of the
	// game menu from rendering it.
	if (m_GameState != Game::PAUSED)
	{
		m_GameMenu->Update(a_DT);
	}

	m_Grid->Render(a_DT);

	if (m_GameState == Game::PAUSED)
	{
		Iw2DSetColour(0xaa000000);
		Iw2DFillRect(CIwSVec2(0, 0), CIwSVec2(Iw2DGetSurfaceWidth(), Iw2DGetSurfaceHeight()));
		Iw2DSetColour(0xffffffff);
		m_PauseMenu->Render();
	}
	else if (m_GameState == Game::NORMAL)
	{
		m_GameMenu->Render();
	}
	else if (m_GameState == Game::GAMEOVER)
	{
		Iw2DSetColour(0xaa000000);
		Iw2DFillRect(CIwSVec2(0, 0), CIwSVec2(Iw2DGetSurfaceWidth(), Iw2DGetSurfaceHeight()));
		Iw2DSetColour(0xffffffff);
		m_GameOverMenu->Render();
	}
}

Game::T_GAMESTATE Game::GetGameState()
{
	return m_GameState;
}

void Game::SwitchGameState( Game::T_GAMESTATE a_State )
{
	switch (a_State)
	{
	case Game::LINEANIMATING:
		{
			break;
		}
	case Game::NORMAL:
		{
			break;
		}
	case Game::GAMEOVER:
		{
			break;
		}
	case Game::PAUSED:
		{
			break;
		}
	}

	m_GameState = a_State;
}

void Game::IncrementScore( int a_Points )
{
	m_Score += a_Points;

	if (m_Score > Engine::GetSingletonPtr()->GetCurrentHighscore())
	{
		Engine::GetSingletonPtr()->SetHighscore(m_Score);
	}
}

int Game::GetScore()
{
	return m_Score;
}

void Game::Restart()
{
	if (m_Grid)
		delete m_Grid;
	m_Grid = new Grid();

	m_BlockMoveInterval = GAME_MOVE_INTERVAL_MAX - ((GAME_MOVE_INTERVAL_MAX - GAME_MOVE_INTERVAL_MIN) / 3);
	m_Score = 0;

	SwitchGameState(Game::NORMAL);
}

Grid* Game::GetGrid()
{
	return m_Grid;
}

void Game::ConstructGameMenu()
{
	// --------- General page.
	UI::Page* page = new UI::Page( "GameMenu_General" );

	UI::Button* button = new UI::Button( CIwFVec2(0.67f, 0.895f), CIwFVec2(0.30f, 0.08f), "btn_game_menu", "btn_game_menu_down", NULL, &btnFunction_PauseGame );
	page->AddWidget(button);

	m_GameMenu->AddPage(page);
}

void Game::ConstructPauseMenu()
{
	// --------- General page.
	UI::Page* page = new UI::Page( "PauseMenu_General" );

	UI::Image* image = NULL;
	
	UI::Button* button = new UI::Button( CIwFVec2(0.10f, 0.185f), CIwFVec2(0.78f, 0.12f), "btn_game_resume", "btn_game_resume_down", NULL, &btnFunction_ResumeGame );
	page->AddWidget(button);

	button = new UI::Button( CIwFVec2(0.17f, 0.335f), CIwFVec2(0.65f, 0.1f), "btn_menu_options", "btn_menu_options_down", NULL, &btnFunction_GoToGameOptions );
	page->AddWidget(button);

	button = new UI::Button( CIwFVec2(0.17f, 0.485f), CIwFVec2(0.65f, 0.1f), "btn_game_help", "btn_game_help_down", NULL, &btnFunction_GoToHelp );
	page->AddWidget(button);

	button = new UI::Button( CIwFVec2(0.17f, 0.635f), CIwFVec2(0.65f, 0.1f), "btn_game_restart", "btn_game_restart_down", NULL, &btnFunction_RestartGame );
	page->AddWidget(button);

	button = new UI::Button( CIwFVec2(0.17f, 0.785f), CIwFVec2(0.65f, 0.1f), "btn_game_quit", "btn_game_quit_down", NULL, &btnFunction_QuitGame );
	page->AddWidget(button);

	m_PauseMenu->AddPage(page);


	// --------- Help page.	
	page = new UI::Page( "PauseMenu_Help" );

	image = new UI::Image( CIwFVec2(0.0f, 0.082f), CIwFVec2(1.0f, 0.75f), "img_help_instructions" );
	page->AddWidget(image);

	button = new UI::Button( CIwFVec2(0.67f, 0.85f), CIwFVec2(0.30f, 0.08f), "btn_menu_back", "btn_menu_back_down", NULL, &btnFunction_GoToGeneralPause );
	page->AddWidget(button);

	m_PauseMenu->AddPage(page);
	
	// --------- Options page.
	page = new UI::Page( "PauseMenu_Options" );

	button = new UI::Button( CIwFVec2(0.55f, 0.85f), CIwFVec2(0.35f, 0.1f), "btn_menu_back", "btn_menu_back_down", NULL, &btnFunction_GoToGeneralPause );
	page->AddWidget(button);

	image = new UI::Image( CIwFVec2(0.125f, 0.282f), CIwFVec2(0.75f, 0.04f), "img_slider_title_sound" );
	page->AddWidget(image);

	UI::Slider* slider = new UI::Slider( CIwFVec2(0.125f, 0.332f), CIwFVec2(0.75f, 0.1f), "slider", Engine::GetSingletonPtr()->m_MusicVolume, Engine::GetSingletonPtr()->m_MinMusicVolume, Engine::GetSingletonPtr()->m_MaxMusicVolume, NULL, NULL );
	page->AddWidget(slider);


	image = new UI::Image( CIwFVec2(0.125f, 0.482f), CIwFVec2(0.75f, 0.04f), "img_slider_title_gamespeed" );
	page->AddWidget(image);

	slider = new UI::Slider( CIwFVec2(0.125f, 0.532f), CIwFVec2(0.75f, 0.1f), "slider", m_BlockMoveInterval, GAME_MOVE_INTERVAL_MIN, GAME_MOVE_INTERVAL_MAX, true, NULL, NULL );
	page->AddWidget(slider);

	m_PauseMenu->AddPage(page);
}

void Game::ConstructGameOverMenu()
{
	// --------- General page.
	UI::Page* page = new UI::Page( "GameOverMenu_General" );

	UI::Button* button = new UI::Button( CIwFVec2(0.10f, 0.535f), CIwFVec2(0.78f, 0.12f), "btn_game_restart", "btn_game_restart_down", NULL, &btnFunction_RestartGame );
	page->AddWidget(button);

	button = new UI::Button( CIwFVec2(0.17f, 0.785f), CIwFVec2(0.65f, 0.1f), "btn_game_quit", "btn_game_quit_down", NULL, &btnFunction_QuitGame );
	page->AddWidget(button);

	UI::Image* image = new UI::Image( CIwFVec2(0.0f, 0.285f), CIwFVec2(1.0f, 0.08f), "img_gameover");
	page->AddWidget(image);

	m_GameOverMenu->AddPage(page);
}

Game::Game()
	: m_GameState(Game::NORMAL)
	, m_PauseMenu(new UI::Menu())
	, m_GameMenu(new UI::Menu())
	, m_GameOverMenu(new UI::Menu())
	, m_Grid(new Grid())
	, m_SwipeObject(new SwipeObject(0))
	, m_SwipeMargin( CIwSVec2( int16(float(Iw2DGetSurfaceWidth()) / 10), int16(float(Iw2DGetSurfaceHeight()) / 10)) )
	, m_BlockMoveInterval( GAME_MOVE_INTERVAL_MAX - ((GAME_MOVE_INTERVAL_MAX - GAME_MOVE_INTERVAL_MIN) / 3) )
	, m_Score(0)
	, m_CanRotate(true)
	, m_IsPerformingSwipe(false)
{
	SetSingletonPtr(this);

	ConstructGameMenu();
	ConstructPauseMenu();
	ConstructGameOverMenu();

	m_PreviewBG = Iw2DCreateImageResource("preview_bg");

	const float PreviewBGScaleX = (( 4 * BlockSize + 5 ) / 153.0f);
	const float PreviewBGScaleY = (( 4 * BlockSize + 5 ) / 153.0f);
	m_PreviewTopLeft = CIwSVec2( int16( TO_SCREENX(452.0f)), 
								 int16( Iw2DGetSurfaceHeight() - TO_SCREENY(27.0f) - (20 * BlockSize + 21) + (55.0f * PreviewBGScaleY) ) );
	m_PreviewBGTopLeft = m_PreviewTopLeft - CIwSVec2(	int16(21.0f * PreviewBGScaleX),
														int16(55.0f * PreviewBGScaleY) );
	m_PreviewBGSize = CIwSVec2(  int16(196.0f * PreviewBGScaleX), 
								 int16(214.0f * PreviewBGScaleY) );
}

Game::~Game()
{
	if (m_PauseMenu)
	{
		m_PauseMenu->Delete();
		delete m_PauseMenu;
	}
	m_PauseMenu = NULL;

	if (m_GameMenu)
	{
		m_GameMenu->Delete();
		delete m_GameMenu;
	}
	m_GameMenu = NULL;

	if (m_GameOverMenu)
		m_GameOverMenu->Delete();
		delete m_GameOverMenu;
	m_GameOverMenu = NULL;

	if (m_SwipeObject)
		delete m_SwipeObject;
	m_SwipeObject = NULL;

	if (m_Grid)
		delete m_Grid;
	m_Grid = NULL;

	if (m_PreviewBG)
		delete m_PreviewBG;
	m_PreviewBG = NULL;
}